package com.ruoyi.common.utils;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class IdSeqGenerator {
    //日期起始点
    private static final long twepoch            = 1463108596098L;

    private final long        workerId;
    private long              sequence           = 0L;
    private long              lastTimestamp      = -1L;
    //机器ID占用10bits
    private static final long workerIdBits       = 10L;
    //序列占用12bits
    private static final long sequenceBits       = 12L;
    //机器ID 最大值
    private static final long  maxWorkerId        = -1L ^ -1L << workerIdBits;
    //时间偏移位
    private static final long timestampLeftShift = sequenceBits + workerIdBits;
    //机器ID偏移位
    private static final long workerIdShift      = sequenceBits;
    //序列掩码
    private static final long  sequenceMask       = -1L ^ -1L << sequenceBits;

    public static IdSeqGenerator idSeqGenerator  = null;

    /**
     * @description: 初始化
     * @author zhoudong
     * @date 2020/5/21 14:38
     */
    public static IdSeqGenerator getInstance(final long workerId){
        if(idSeqGenerator == null){
            synchronized (IdSeqGenerator.class){
                idSeqGenerator = new IdSeqGenerator(workerId);
            }
        }
        return idSeqGenerator;
    }
    /**
     * @description: 无参初始化
     * @author zhoudong
     * @date 2020/5/21 14:38
     */
    public static synchronized IdSeqGenerator getInstance(){
        if(idSeqGenerator == null){
            idSeqGenerator = new IdSeqGenerator((long) (1+Math.random()*1000));
        }
        return idSeqGenerator;
    }
    /**
     * @description: 构造
     * @author zhoudong
     * @date 2020/5/21 14:38
     */
    public IdSeqGenerator(final long workerId) {
        super();
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        this.workerId = workerId;
    }
    /**
     * @description: 生成ID
     * @author zhoudong
     * @date 2020/5/21 14:38
     */
    public long getId() {
        long timestamp = this.timeGen();
        //如果是同一时间生成的，则自增
        if (this.lastTimestamp == timestamp) {
            this.sequence = (this.sequence + 1) & sequenceMask;
            if (this.sequence == 0) {
                //生成下一个毫秒级的序列
                timestamp = this.tilNextMillis(this.lastTimestamp);
            }
        } else {
            //如果发现是下一个时间单位，则自增序列回0，重新自增
            this.sequence = 0;
        }
        if (timestamp < this.lastTimestamp) {
            try {
                throw new Exception(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", this.lastTimestamp - timestamp));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        this.lastTimestamp = timestamp;
        long nextId = (timestamp - twepoch << timestampLeftShift) | (this.workerId << workerIdShift) | (this.sequence);
        return nextId;
    }
    /**
     * @description: 获取下个时间戳
     * @author zhoudong
     * @date 2020/5/21 14:38
     */
    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }
    /**
     * @description: 获取当前时间戳
     * @author zhoudong
     * @date 2020/5/21 14:38
     */
    private long timeGen() {
        return System.currentTimeMillis();
    }
    /**
     * @description: 测试
     * @author zhoudong
     * @date 2020/5/21 14:39 
     */
    public static void main(String[] args) {

        final int MAX_POOL_SIZE = 4;
        ExecutorService service = Executors.newFixedThreadPool(MAX_POOL_SIZE);
        for (int i = 0; i < MAX_POOL_SIZE; i++) {
            service.execute(()->{
                for (int j = 0; j < 1000; j++) {
                    IdSeqGenerator.getInstance().getId();
                }
            });
        }

        service.shutdown();
    }

}
